﻿#pragma once

#include <QMap>
#include <QMultiMap>
#include <QVariant>
#include <mutex>
#include "../PublicService/ErrLog.h"

namespace BusinessLayer
{

/**
 * @brief 数据池。本类对象由ContainerForDevice生成并管理。
 *        数据池的作用：让所有Device可以将自己当前采集的数据、状态统一放到数据池里，供呈现层或者业务层的模块使用。
 *        数据池将数据按 “设备对象名.字段名” 的形式作为key，以QVariant类型作为数据，保存到Map中。
 *        要访问数据池中的数据，可调用put方法。
 *        注意：数据池的get和put函数是线程安全的。
 */
class DataPool
{
private:
    QMap<QString, QVariant> dataRepo;   //数据存储
//    QMap<QString, std::function<void(QVariant)>> callBacks; //数据发生put操作时，被通知者的回调函数映射。
    QMultiMap<QString, std::function<void(QVariant)>> callBacks;
    std::mutex mtx;

public:
    DataPool() {}
    DataPool(DataPool&) = delete;

    /**
     * @brief 注册当某个数据发生改变（put操作）时，要通知的对象的回调函数。如果注册了这个函数，比如，将Volt对象的value值
     *        （Volt.value)注册映射到MyWindow::on_volt_changed函数，则：当Volt.value发生重新写入时，MyWindow::on_volt_changed
     *        就会被调用。
     *        注意：对于任何对象（尤其是ui构件）内的函数或者Lambda表达式，当该对象析构时，需要调用UnregisterDataCallback取消注册。
     * @param objFieldName 以 “对象名.字段名” 为形式的数据名
     * @param callBack 回调函数
     */
    inline void RegisterDataCallback(const QString& objFieldName, std::function<void(QVariant)> callBack)
    {
        std::unique_lock<std::mutex> lock(mtx);
        callBacks.insertMulti(objFieldName, callBack);
    }

    /**
     * @brief 对于在对象内使用RegisterDataCallback注册的数据名-回调函数注册的数据通知，
     *        当该对象析构时，需要调用UnregisterDataCallback取消注册。
     * @param objFieldName 以 “对象名.字段名” 为形式的数据名
     */
    inline void UnregisterDataCallback(const QString& objFieldName)
    {
        std::unique_lock<std::mutex> lock(mtx);
        callBacks.remove(objFieldName);
    }

    /**
     * @brief 将数据放入数据池。
     * @param objName 设备对象的名称。这个名称来自于SettingData.xml中对Device的name属性的定义。
     * @param fieldName 设备要存储的设备数据的字段名。
     * @param value 要存入的值
     */
    inline void put(const QString& objName, const QString& fieldName, const QVariant& value)
    {
        QString objFieldName = objName + "." + fieldName;
        std::unique_lock<std::mutex> lock(mtx);
        QVariant valueSave = dataRepo[objFieldName];
        dataRepo[objFieldName] = value;
        auto callBackList = callBacks.values(objFieldName);
        if (callBackList.size() != 0 && value != valueSave)
        {
            for (auto& callBack : callBackList)
            {
                try
                {
                    callBack(value);
                }
                catch (...)
                {
                    LOGERR("DataPool::put", "调用回调函数时发生异常。数据名：" + objFieldName);
                }
            }
        }
    }

    /**
     * @brief 从数据池中获取数据。
     * @param objName 设备对象的名称。这个名称来自于SettingData.xml中对Device的name属性的定义。
     * @param fieldName 要获取的设备数据的字段名。
     * @return 数据值
     */
    inline QVariant get(const QString& objName, const QString& fieldName)
    {
        std::unique_lock<std::mutex> lock(mtx);
        return dataRepo[objName + "." + fieldName];
    }

    /**
     * @brief 从数据池中获取数据。
     * @param objFieldName 以 “设备对象名.字段名” 为形式，指定要访问的数据
     * @return 数据值
     */
    inline QVariant get(const QString& objFieldName)
    {
        std::unique_lock<std::mutex> lock(mtx);
        return dataRepo[objFieldName];
    }
};

}
